package com.zzyl.nursing.service.impl;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.job.AmqpClient;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IDeviceService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.nursing.webSocketConfig.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.service.IAlertRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import static com.zzyl.common.constant.CacheConstants.*;

/**
 * 报警规则Service业务层处理
 *
 * @author ruoyi
 * @date 2025-09-21
 */
@Service
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService {
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IAlertDataService alertDataService;

    @Autowired
    private DeviceMapper deviceMapper;


    private String roleName = "维修工";
    private String admin = "超级管理员";
    @Autowired
    private IDeviceService deviceService;

    /**
     * 查询报警规则
     *
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id) {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     *
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule) {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule) {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule) {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则
     *
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则信息
     *
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id) {
        return removeById(id) ? 1 : 0;
    }

    /**
     * 报警数据检测
     */
    @Override
    public void alertFilter() {

        // 1、判断规则是否为空
        List<AlertRule> alertRulesList = this.lambdaQuery().eq(AlertRule::getStatus, 1).list();
        if (ObjectUtil.isEmpty(alertRulesList)) {
            return;
        }
        // 2、判断数据是否为空，或者过期
        List values = redisTemplate.opsForHash().values(IOT_DEVICE_LAST_DATA);
        if (ObjectUtil.isEmpty(values)) {
            return;
        }
        // 3、对数据进行处理，拆分后添加到一个集合中
        ArrayList<DeviceData> checkDateList = new ArrayList<>();
        values.forEach(value -> {
                    checkDateList.addAll((List<DeviceData>) value);
                }
        );
        if (ObjectUtil.isEmpty(checkDateList)) {
            return;
        }

        checkDateList.forEach(
                // 4、根据规则判断是否为异常数据
                deviceData -> alertCheck(deviceData));

    }

    /**
     * 5、根据规则判断是否为异常数据
     *
     * @param deviceData
     */
    private void alertCheck(DeviceData deviceData) {
        // 5.1 判断数据是否超时
        long between = LocalDateTimeUtil.between(deviceData.getAlarmTime(), LocalDateTime.now(), ChronoUnit.SECONDS);
        if (between > 60) {
            return;
        }
        // 查询和数据关联的规则
        String iotId = deviceData.getIotId();
        //查询该产品下的通用规则
        List<AlertRule> allRules = this.lambdaQuery().eq(AlertRule::getStatus, 1)
                .eq(AlertRule::getProductKey, deviceData.getProductKey())
                .eq(AlertRule::getIotId, "-1")
                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                .list();

        //查询该设备独有规则
        List<AlertRule> privateRules = this.lambdaQuery().eq(AlertRule::getStatus, 1)
                .eq(AlertRule::getProductKey, deviceData.getProductKey())
                .eq(AlertRule::getIotId, deviceData.getIotId())
                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                .list();

        Collection<AlertRule> alertRules = CollUtil.addAll(allRules, privateRules);
        if (ObjectUtil.isEmpty(alertRules)) {
            return;
        }
        //遍历规则，判断数据是否需要报警
        alertRules.forEach(r -> {
            dataCheck(r, deviceData);
        });
    }

    /**
     * 根据规则判断数据是否需要报警
     *
     * @param rule
     * @param deviceData
     */
    private void dataCheck(AlertRule rule, DeviceData deviceData) {
        //判断规则是否在生效范围内
        String[] split = rule.getAlertEffectivePeriod().split("~");
        LocalTime startTime = LocalTime.parse(split[0], DateTimeFormatter.ofPattern("HH:mm:ss"));
        LocalTime endTime = LocalTime.parse(split[1], DateTimeFormatter.ofPattern("HH:mm:ss"));
        LocalTime alarmTime = deviceData.getAlarmTime().toLocalTime();
        if (alarmTime.isBefore(startTime) || alarmTime.isAfter(endTime)) {
            return;
        }
        //是否到达阈值
        // 1、组装key  设备id 功能id 规则id
        StringBuilder key = new StringBuilder();
        key.append(deviceData.getIotId());
        key.append(deviceData.getFunctionId());
        key.append(rule.getId());
        String durationKey = IOT_COUNT_ALERT + key.toString();
        //2、判断
        Double dataValue = Double.valueOf(deviceData.getDataValue());
        String operator = rule.getOperator();
        if ("<".equals(operator) && dataValue < rule.getValue() || (">=".equals(operator)) && dataValue >= rule.getValue()) {
            //触发报警
            List<Long> ids = triggerAlarm(rule, deviceData, key);
            if (ObjectUtil.isEmpty(ids)) {
                return;
            }
            //保存到数据库
            saveAlertDate(ids, rule, deviceData);
        } else {
            //否则删除报警记录，破坏连续性原则
            redisTemplate.delete(durationKey);
        }
    }

    //数据保存
    private void saveAlertDate(List<Long> ids, AlertRule rule, DeviceData deviceData) {
        AlertData alertData = BeanUtil.toBean(deviceData, AlertData.class);
        alertData.setId(null);
        String reason = deviceData.getFunctionId() + rule.getOperator() + rule.getValue() + "持续了" + rule.getDuration() + "分钟";
        alertData.setAlertReason(reason);
        alertData.setAlertRuleId(rule.getId());
        alertData.setStatus(0);
        alertData.setType(rule.getAlertDataType());
        ArrayList<AlertData> alertDates = new ArrayList<>();
        for (Long id : ids) {
            AlertData data = BeanUtil.toBean(alertData, AlertData.class);
            data.setUserId(id);
            alertDates.add(data);
        }
//        log.warn("开始报警");
        alertDataService.saveBatch(alertDates);
        //推送给前端
        websocketNotity(alertDates, rule, ids);
    }

    //WebSocket推送给前端
    @Autowired
    private WebSocketServer webSocketServer;

    private void websocketNotity(ArrayList<AlertData> alertDates, AlertRule rule, List<Long> ids) {
        AlertNotifyVo alertNotifyVo = BeanUtil.toBean(alertDates.get(0), AlertNotifyVo.class);
        alertNotifyVo.setFunctionName(rule.getFunctionName());
        alertNotifyVo.setAlertDataType(rule.getAlertDataType());
        alertNotifyVo.setNotifyType(1);
        //发送给前端
        log.error("发给前端");
        webSocketServer.sendMessageToConsumer(alertNotifyVo, ids);

    }

    /**
     * 触发报警
     *
     * @param rule
     * @param deviceData
     * @param key
     */
    private List<Long> triggerAlarm(AlertRule rule, DeviceData deviceData, StringBuilder key) {
        //判断是否在沉默周期
        String silentKey = IOT_SILENT_ALERT + key.toString();
        Object silentAlert = redisTemplate.opsForValue().get(silentKey);
        if (ObjectUtil.isNotEmpty(silentAlert)) {
            return null;
        } else {
            //检测是否到达持续周期
            String durationKey = IOT_COUNT_ALERT + key.toString();
            Long increment = redisTemplate.opsForValue().increment(durationKey);
            if (increment >= rule.getDuration()) {
//                log.error("开始报警！！");
                //删除计数
                redisTemplate.delete(durationKey);
                //添加沉默周期
                redisTemplate.opsForValue().set(silentKey,"1",rule.getAlertSilentPeriod(), TimeUnit.MINUTES);
                ArrayList<Long> ids = new ArrayList<>();
                // 如果是老人数据
                if (rule.getAlertDataType() == 0) {
                    //床位数据
                    if (deviceData.getLocationType() == 1) {
                        List<Long> users = deviceMapper.selectNursingIdByIotIdWithBed(deviceData.getIotId());
                        ids.addAll(users);
                    }
                    //随身设备
                    else {
                        List<Long> users = deviceMapper.selectNursingIdByIotIdWithElder(deviceData.getIotId());
                        ids.addAll(users);
                    }
                }
                // 如果是设备 ，找维修人员
                else {
                    List<Long> users = deviceMapper.quaryByRoleName(roleName);
                    ids.addAll(users);
                }
                List<Long> users = deviceMapper.quaryByRoleName(admin);
                ids.addAll(users);
                List<Long> distinctIds = CollUtil.distinct(ids);
                return distinctIds;
            }
            return null;
        }
    }
}
